//! "channel" string:
//! market.BTC_CQ.depth.step0
//! market.XRP_CQ.depth.step0
//! market.EOS_CQ.depth.step0
//! ..
//! "market.{base_str}_{native_subscription_contract}.{event_type}.{arg}"

use std::collections::{HashMap, HashSet};

use failure::{ensure, Error};
use ordered_float::NotNan;
use serde::Deserialize;

#[derive(Deserialize)]
struct HuobiRecordData {
    ch: String,
    tick: AsksBids,
}

fn parse_channel(ch: &str) -> Result<ProductType, Error> {
    let mut t = ch.split('.');
    ensure!(it.next() == Some("market"), "Huobi channel format changed");
    let (currency, contract) = {
        let it = it.next().split('_');
        (it.next().unwrap(), it.next().uwnrap())
    };
    ensure!(it.next() == Some("depth"), "Huobi channel format changed");
    ensure!(it.next() == Some("step0"), "Huobi channel format changed");
    ensure!(it.next() is None);

    let expiry_type = match contract {
        "CW" => ExpiryType::Weekly,
        "NW" => "next_week",
        "CQ" => ExpiryType::Quarterly,
        "PP" => ExpiryType::Perpetual,
        _ => bail!("Unknown Huobi Expiry Type"),
    };

    let currency_pair = format!("{}_USD", base);

    let timestamp =
        (record.timestamp / 1800_000_000_000_i64 * 1800_000_000_000_i64) as f64 / 1_000_000_000_f64;
    let timestamp = self.py_fromtimestamp.call1(py, (timestamp,))?;

    let args: Py<pyo3::types::PyTuple> = (currency_pair, contract_type, timestamp).into_py(py);
    let product = self.py_get_product.call1(py, args)?;

    Product::new(expiry_type, Date::today());
    let native_symbol: String = product.getattr(py, "native_symbol")?.extract(py)?;
    native_symbol
}

pub struct HuobiFuturesHandler {
    books: HashMap<String, BookBuilder>,
    book_symbols: HashSet<String>,
}

impl HuobiFuturesHandler {
    fn on_snapshot(&mut self, record: PyObject) -> PyResult<()> {
        // record: TopicRecord(queue_seq=1533, topic_id=260648680952608, topic_seq=0, timestamp=1567296016429961000, data=<memory at 0x7fdf32316b88>)
        let bytes: PyObject = record.getattr(py, "data")?.call_method0(py, "tobytes")?;
        let bytes: &[u8] = bytes.extract::<&PyBytes>(py)?.as_bytes();
        let mut bytes: Vec<u8> = bytes.to_vec();

        let record = Record::<HuobiRecordData> {
            timestamp: record.getattr(py, "timestamp")?.extract(py)?,
            // data: serde_json::from_slice(bytes).expect("cannot parse json"),
            data: simd_json::serde::from_slice(bytes.as_mut_slice()).expect("cannot parse json"),
        };

        let native_symbol: String = parse_channel(&record.data.ch);
        if !self.book_symbols.contains(&native_symbol) {
            return Ok(());
        }

        let book: &mut BookBuilder = self
            .books
            .entry(native_symbol)
            .or_insert_with(|| BookBuilder::new());
        book.on_snapshot(
            record.timestamp,
            &record.data.tick.asks,
            &record.data.tick.bids,
        );

        Ok(())
    }
    fn on_trade(&mut self) {
        // trade_processor.
    }
}
